package com.eckey.lab.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.eckey.lab.entity.Constants;
import com.eckey.lab.entity.Hotel;
import com.eckey.lab.entity.Page;
import com.eckey.lab.entity.bo.HotelBo;
import com.eckey.lab.entity.bo.QueryParams;
import com.eckey.lab.service.IHotelService;
import com.jayway.jsonpath.internal.function.numeric.AbstractAggregation;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.junit.platform.commons.util.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: Marinc
 * @CreateTime: 2023-07-06  10:21
 * @Description: TODO
 * @Version: 1.0
 */
@Slf4j
@Service
public class HotelServiceImpl implements IHotelService {

    @Resource
    private RestHighLevelClient restHighLevelClient;

    //根据条件查询list
    public Page queryListByParams(QueryParams queryParams) {
        SearchRequest searchRequest = new SearchRequest(Constants.HOTEL_INDEX);
        Page page = new Page();
        page.setPageNo(queryParams.getPageNo());
        page.setPageSize(queryParams.getPageSize());

        //
        buildParams(queryParams, searchRequest);

        Integer start = 0;
        if (queryParams.getPageNo() != null && queryParams.getPageSize() != null) {
            start = (queryParams.getPageNo() - 1) * queryParams.getPageSize();
        }
        page.setStart(start);
        searchRequest.source().from(start);
        if (queryParams.getPageSize() != null) {
            searchRequest.source().size(queryParams.getPageSize());
        } else {
            searchRequest.source().size(10);
        }
        SearchHits hits = null;
        TotalHits totalHits = null;
        try {
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            log.info("result:{}", response);
            hits = response.getHits();
            totalHits = hits.getTotalHits();
        } catch (IOException e) {
            e.printStackTrace();
        }
        page.setTotal(totalHits.value);
        log.info("总数为total:{}", totalHits);
        List<Hotel> lists = new ArrayList<Hotel>();
        SearchHit[] hitsList = hits.getHits();
        for (SearchHit documentFields : hitsList) {
            String sourceAsString = documentFields.getSourceAsString();
            log.info("查询结果：{}", sourceAsString);
            if (StringUtils.isNotBlank(sourceAsString)) {
                Hotel hotel = JSON.parseObject(sourceAsString, Hotel.class);
                Object[] sortValues = documentFields.getSortValues();
                if (sortValues.length != 0) {
                    hotel.setDistance(sortValues[0].toString());
                }
                lists.add(hotel);
            }
        }
        page.setData(lists);
        return page;
    }

    public boolean queryExitsHotel() {
        try {
            GetIndexRequest request = new GetIndexRequest(Constants.HOTEL_INDEX);
            boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
            log.info("exists :{}", (exists ? "索引库已经存在" : "索引库不存在"));
            return exists;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public List<HotelBo> countListByCity() {
        //
        SearchRequest request = new SearchRequest(Constants.HOTEL_INDEX);
        request.source().size(0);
        request.source().aggregation(AggregationBuilders.terms("cityAgg").field("city").size(20));
        List<HotelBo> hotelBos = new ArrayList<HotelBo>();
        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            log.info("请求返回结果：{}", response);
            Aggregations aggregations = response.getAggregations();
            Terms terms = aggregations.get("cityAgg");
            List<? extends Terms.Bucket> buckets = terms.getBuckets();
            for (Terms.Bucket bucket : buckets) {
                HotelBo hotelBo = new HotelBo();
                hotelBo.setCity(bucket.getKey().toString());
                hotelBo.setTotal((int) bucket.getDocCount());
                hotelBos.add(hotelBo);
            }
            log.info("hotelbos:{}", hotelBos);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return hotelBos;
    }

    private List<String> analyzingAggregation(List<String> lists, Aggregations aggregations, String aggreName) {
        Terms terms = aggregations.get(aggreName);
        List<? extends Terms.Bucket> buckets = terms.getBuckets();
        for (Terms.Bucket bucket : buckets) {
            lists.add(bucket.getKeyAsString());
        }
        return lists;
    }

    public Map<String, List<String>> countByBrand() {
        SearchRequest request = new SearchRequest(Constants.HOTEL_INDEX);
        request.source().size(0);
        //城市
        request.source().aggregation(AggregationBuilders.terms("cityAgg").field("city").size(20));
        //星级
        request.source().aggregation(AggregationBuilders.terms("starAgg").field("starName").size(20));
        //是否广告投放
        request.source().aggregation(AggregationBuilders.terms("adAgg").field("adFlag").size(20));
        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            log.info("请求返回结果：{}", response);
            Aggregations aggregations = response.getAggregations();
            List<String> cityAgglist = analyzingAggregation(new ArrayList<String>(), aggregations, "cityAgg");
            List<String> starAggList = analyzingAggregation(new ArrayList<String>(), aggregations, "starAgg");
            List<String> adAggList = analyzingAggregation(new ArrayList<String>(), aggregations, "adAgg");
            HashMap<String, List<String>> stringObjectHashMap = new HashMap<String, List<String>>();
            stringObjectHashMap.put("cityAgg", cityAgglist);
            stringObjectHashMap.put("starAggList", starAggList);
            stringObjectHashMap.put("adAggList", adAggList);
            log.info("数据封装结果：{}", stringObjectHashMap);
            return stringObjectHashMap;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //参数组装
    private void buildParams(QueryParams queryParams, SearchRequest searchRequest) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StringUtils.isBlank(queryParams.getKeyWord())) {
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        } else {
            boolQueryBuilder.must(QueryBuilders.matchQuery("all", queryParams.getKeyWord()));
        }
        if (StringUtils.isNotBlank(queryParams.getCity())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("city", queryParams.getCity()));
        }
        if (StringUtils.isNotBlank(queryParams.getHotelName())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("hotelName", queryParams.getHotelName()));
        }
        if (StringUtils.isNotBlank(queryParams.getStarName())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("starName", queryParams.getStarName()));
        }
        if (queryParams.getMaxPrice() != null && queryParams.getMinPrice() != null) {
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gt(queryParams.getMinPrice()).lte(queryParams.getMaxPrice()));
        }
        //searchRequest.source().query(boolQueryBuilder);
        //adFlag为true，权重*10
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(boolQueryBuilder, new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                        QueryBuilders.termQuery("adFlag", true),
                        ScoreFunctionBuilders.weightFactorFunction(10)
                )
        });

        if (StringUtils.isNotBlank(queryParams.getSortBy())) {
            searchRequest.source().sort(queryParams.getSortBy(), SortOrder.ASC);
        }
        //根据指定坐标查询附近酒店
        if (StringUtils.isNotBlank(queryParams.getLocation())) {
            searchRequest.source().sort(SortBuilders.geoDistanceSort("location",
                    new GeoPoint(queryParams.getLocation())).order(SortOrder.ASC).unit(DistanceUnit.KILOMETERS));
        }
        searchRequest.source().query(functionScoreQueryBuilder);
    }


    public Page countByParams(QueryParams queryParams) {
        SearchRequest searchRequest = new SearchRequest(Constants.HOTEL_INDEX);
        Page page = new Page();
        page.setPageNo(queryParams.getPageNo());
        page.setPageSize(queryParams.getPageSize());

        //组装参数
        buildParams(queryParams, searchRequest);

        Integer start = 0;
        if (queryParams.getPageNo() != null && queryParams.getPageSize() != null) {
            start = (queryParams.getPageNo() - 1) * queryParams.getPageSize();
        }
        page.setStart(start);
        searchRequest.source().size(0);
        searchRequest.source().aggregation(AggregationBuilders.terms("cityAgg").field("city").size(20));
        //星级
        searchRequest.source().aggregation(AggregationBuilders.terms("starAgg").field("starName").size(20));
        //是否广告投放
        searchRequest.source().aggregation(AggregationBuilders.terms("adAgg").field("adFlag").size(20));
        try {
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            log.info("请求返回结果：{}", response);
            Aggregations aggregations = response.getAggregations();
            List<String> cityAgglist = analyzingAggregation(new ArrayList<String>(), aggregations, "cityAgg");
            List<String> starAggList = analyzingAggregation(new ArrayList<String>(), aggregations, "starAgg");
            List<String> adAggList = analyzingAggregation(new ArrayList<String>(), aggregations, "adAgg");
            HashMap<String, List<String>> stringObjectHashMap = new HashMap<String, List<String>>();
            stringObjectHashMap.put("cityAgg", cityAgglist);
            stringObjectHashMap.put("starAggList", starAggList);
            stringObjectHashMap.put("adAggList", adAggList);
            log.info("数据封装结果：{}", stringObjectHashMap);
            page.setData(stringObjectHashMap);
            return page;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public List<String> searchByPinyin(String string) {
        SearchRequest searchRequest = new SearchRequest(Constants.HOTEL_INDEX);
        searchRequest.source().suggest(new SuggestBuilder().addSuggestion("suggestions",
                SuggestBuilders.completionSuggestion("suggestions").prefix(string).skipDuplicates(true).size(10)));
        List<String> lists = new ArrayList<String>();
        try {
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            Suggest suggest = response.getSuggest();
            Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>> titleSuggest = suggest.getSuggestion("suggestions");
            String name = titleSuggest.getName();
            log.info("name:{}",name);
            for (Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option> options : titleSuggest.getEntries()) {
                log.info("options:{}",options);
                List<? extends Suggest.Suggestion.Entry.Option> options1 = options.getOptions();
                for (Suggest.Suggestion.Entry.Option option : options1) {
                    log.info("option1:{}",option);
                    lists.add(option.getText().toString());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return lists;
    }
}
